package com.zerowidth.moc.web.pay.wx;

import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.HttpClientBuilder;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.text.SimpleDateFormat;
import java.util.*;

public class WXUtil {

    /**
     * 执行 POST 方法的 HTTP 请求
     */
    public static String executeHttpPost(String url, SortedMap<String, String> parameters) throws IOException {
        HttpClient httpClient = HttpClientBuilder.create().build();
        HttpPost request = new HttpPost(url);
        request.setHeader("Content-type", "application/xml");
        request.setHeader("Accept", "application/xml");
        request.setEntity(new StringEntity(XmlUtils.transferMapToXml(parameters), "UTF-8"));
        HttpResponse response = httpClient.execute(request);
        return readResponse(response);
    }

    /**
     * 读取 response body 内容为字符串
     */
    public static String readResponse(HttpResponse response) throws IOException {
        BufferedReader in = new BufferedReader(
                new InputStreamReader(response.getEntity().getContent()));
        String result = "";
        String line;
        while ((line = in.readLine()) != null) {
            result += line;
        }
        return result;
    }

    /**
     * 获取当前时间 yyyyMMddHHmmss
     */
    public static String getCurrTime() {
        Date now = new Date();
        SimpleDateFormat outFormat = new SimpleDateFormat("yyyyMMddHHmmss");
        String s = outFormat.format(now);
        return s;
    }

    /**
     * 获取随机字符串
     */
    public static String getRandomStr() {
        // 随机数
        String currTime = WXUtil.getCurrTime();
        // 8位日期
        String strTime = currTime.substring(8, currTime.length());
        // 四位随机数
        String strRandom = WXUtil.buildRandom(4) + "";
        // 10位序列号,可以自行调整。
        return strTime + strRandom;
    }

    /**
     * 取出一个指定长度大小的随机正整数.
     *
     * @param length int 设定所取出随机数的长度。length小于11
     * @return int 返回生成的随机数。
     */
    public static int buildRandom(int length) {
        int num = 1;
        double random = Math.random();
        if (random < 0.1) {
            random = random + 0.1;
        }
        for (int i = 0; i < length; i++) {
            num = num * 10;
        }
        return (int) ((random * num));
    }

    /**
     * 第一次签名
     *
     * @param parameters 数据为服务器生成，下单时必须的字段排序签名
     * @param key        商户的密钥
     * @return
     */
    public static String createSign(SortedMap<String, String> parameters, String key) {
        StringBuffer sb = new StringBuffer();
        Set es = parameters.entrySet();//所有参与传参的参数按照accsii排序（升序）
        Iterator it = es.iterator();
        while (it.hasNext()) {
            Map.Entry<String, String> entry = (Map.Entry) it.next();
            String k = entry.getKey();
            String v = entry.getValue();
            if (null != v && !"".equals(v)
                    && !"sign".equals(k) && !"key".equals(k)) {
                sb.append(k + "=" + v + "&");
            }
        }
        sb.append("key=" + key);
        return encodeMD5(sb.toString());
    }

    /**
     * 第二次签名
     *
     * @param result 数据为微信返回给服务器的数据（XML 的 String），再次签名后传回给客户端（APP）使用
     * @param key    密钥
     * @return
     * @throws IOException
     */
    public static Map<String, String> createSign2(String result, String key) throws IOException {
        SortedMap<String, String> map = new TreeMap<>(XmlUtils.transferXmlToMap(result));
        Map<String, String> app = new HashMap<>();

        if (map.get("return_code") != null && "FAIL".equals(map.get("return_code"))) {
            app.put("return_code", map.get("return_code"));
            app.put("return_msg", map.get("return_msg"));
        } else {
            app.put("appid", map.get("appid"));
            app.put("partnerid", map.get("mch_id"));
            app.put("prepayid", map.get("prepay_id"));
            app.put("package", "Sign=WXPay");                   // 固定字段，保留，不可修改
            app.put("noncestr", map.get("nonce_str"));
            app.put("timestamp", new Date().getTime() / 1000 + "");  // 时间为秒，JDK 生成的是毫秒，故除以 1000
            app.put("sign", createSign(new TreeMap<>(app), key));
        }
        return app;
    }

    /**
     * MD5 签名
     *
     * @return 签名后的字符串信息
     */
    public static String encodeMD5(String str) {
        try {
            MessageDigest messageDigest = MessageDigest.getInstance("MD5");
            byte[] inputByteArray = (str).getBytes();
            messageDigest.update(inputByteArray);
            byte[] resultByteArray = messageDigest.digest();
            return byteArrayToHex(resultByteArray);
        } catch (NoSuchAlgorithmException e) {
            return null;
        }
    }

    /**
     * 辅助 encodeMD5 方法实现
     */
    private static String byteArrayToHex(byte[] byteArray) {
        char[] hexDigits = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
        char[] resultCharArray = new char[byteArray.length * 2];
        int index = 0;
        for (byte b : byteArray) {
            resultCharArray[index++] = hexDigits[b >>> 4 & 0xf];
            resultCharArray[index++] = hexDigits[b & 0xf];
        }
        // 字符数组组合成字符串返回
        return new String(resultCharArray);
    }

    /**
     * 验证签名是否正确
     *
     * @return boolean
     * @throws Exception
     */
    public static boolean checkSign(SortedMap<String, String> parameters) throws Exception {
        String signWx = parameters.get("sign");
        if (signWx == null) return false;
        parameters.remove("sign"); // 需要去掉原 map 中包含的 sign 字段再进行签名
        String signMe = createSign(parameters, WXOrderFactory.PARTER_KEY);
        return signWx.equals(signMe);
    }

}
